/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2025-08-08     73554       the first version
 */

#include <rtthread.h>
#include <board.h>

#define DBG_TAG "user_modbus"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>

#include "agile_modbus.h"
#include "agile_modbus_rtu.h"
#include <rs485.h>

#include "user_config.h"

/*********************************************************************************/

/*********************************************************************************/
#define THREADTIMECYCLE            5000
#define MODBUS_M_LEN               256
#define MODBUS_S_LEN               256

#define MODBUS_M_ADDR              1
#define MODBUS_S_ADDR              1

rt_mutex_t g_Modbus_mux = RT_NULL; //互斥锁

uint16_t g_ModbusMasterHoldBuf_u16[MODBUS_M_LEN] = {};
uint8_t  g_ModbusMasterSendBuf_u8[MODBUS_M_LEN * 2] = {};
uint8_t  g_ModbusMasterReadBuf_u8[MODBUS_M_LEN * 2] = {};

uint16_t g_ModbusSlaveHoldBuf_u16[MODBUS_S_LEN] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
uint16_t g_ModbusSHoldInBuf_u16[MODBUS_S_LEN] = {};
uint8_t g_ModbusSHoldBitBuf_u8[MODBUS_S_LEN] = {}; //bit
uint8_t g_ModbusSHoldInBitBuf_u8[MODBUS_S_LEN] = {}; //bit
uint8_t  g_ModbusSlaveSendBuf_u8[MODBUS_S_LEN * 2] = {};
uint8_t  g_ModbusSlaveReadBuf_u8[MODBUS_S_LEN * 2] = {};

/*********************************************************************************/

/*********************************************************************************/

/*********************************************************************************/
/* UserModbusMaster线程入口函数
 */
void G_UserModbusMaster_thread_entry(void *parameter)
{
    int l_SendLen, l_ReadLen, l_State;
    static uint8_t s_CommErrCnt_u8 = 0;

    rt_thread_mdelay(100);

    //agile_modbus
    agile_modbus_rtu_t g_ModbusMsater_s;  //RTU 句柄
    agile_modbus_t *g_pModbusMPar_s = &g_ModbusMsater_s._ctx; //参数

    agile_modbus_rtu_init(&g_ModbusMsater_s, g_ModbusMasterSendBuf_u8, MODBUS_M_LEN, g_ModbusMasterReadBuf_u8, MODBUS_M_LEN);
    agile_modbus_set_slave(g_pModbusMPar_s, 1); //从机地址

    //rs485
    rs485_inst_t *l_pRS485M_s = rs485_create("uart2", 9600, 0, MD_M_CTRL_Pin, 1); //串口2，波特率9600，无校验，控制脚，发送模式1（高发低收）
    if (l_pRS485M_s == RT_NULL)
    {
        LOG_E("create rs485M instance fail.");
    }

    rs485_set_recv_tmo(l_pRS485M_s, 250); //设置超时时间
    if (rs485_connect(l_pRS485M_s) != RT_EOK)
    {
        rs485_destory(l_pRS485M_s);
        LOG_E("rs485M connect fail.");
    }

    while(1)
    {
        //生成报文
        l_SendLen = agile_modbus_serialize_read_registers(g_pModbusMPar_s, 0, 0x0f); //起始地址与长度

        //发送接收
        rt_memset(g_pModbusMPar_s->read_buf, 0, sizeof( g_pModbusMPar_s->read_buf));
        l_ReadLen = rs485_send_then_recv(l_pRS485M_s, (void *)g_pModbusMPar_s->send_buf, l_SendLen, g_pModbusMPar_s->read_buf, g_pModbusMPar_s->read_bufsz);
        if(l_ReadLen == 0)
        {
            LOG_W("modbus master read timeout !");

            if(++s_CommErrCnt_u8 > 2)
            {
                s_CommErrCnt_u8 = 2;
            }
        }
        else
        {
            //校验报文
            l_State = agile_modbus_deserialize_read_registers(g_pModbusMPar_s, l_ReadLen, g_ModbusMasterHoldBuf_u16);
            if(l_State < 0)
            {
                LOG_W("modbus m rx failed !");
                if(l_State != -1)
                {
                    LOG_W("Err code:%d.", -128 - l_State);
                }

                if(++s_CommErrCnt_u8 > 2)
                {
                    s_CommErrCnt_u8 = 2;
                }
            }
            else
            {
                LOG_D("rx: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d.",
                        g_ModbusMasterHoldBuf_u16[0], g_ModbusMasterHoldBuf_u16[1], g_ModbusMasterHoldBuf_u16[2],
                        g_ModbusMasterHoldBuf_u16[3], g_ModbusMasterHoldBuf_u16[4], g_ModbusMasterHoldBuf_u16[5],
                        g_ModbusMasterHoldBuf_u16[6], g_ModbusMasterHoldBuf_u16[7], g_ModbusMasterHoldBuf_u16[8],
                        g_ModbusMasterHoldBuf_u16[9], g_ModbusMasterHoldBuf_u16[10], g_ModbusMasterHoldBuf_u16[11],
                        g_ModbusMasterHoldBuf_u16[12], g_ModbusMasterHoldBuf_u16[13]);

                s_CommErrCnt_u8 = 0;
            }
        }

        rt_thread_mdelay(2000);
    }
}
/*********************************************************************************/

/*********************************************************************************/
/* 初始化UserModbusMaster线程
 *
 */
int G_InitUserModbusMasterThread_i32()
{
    rt_thread_t thread9 = rt_thread_create("UserModbusMaster_serial", G_UserModbusMaster_thread_entry, RT_NULL, 2048, 23, 50);
    if(thread9 != RT_NULL)
    {
        rt_thread_startup(thread9);
        LOG_I("run User Modbus.");
    }
    else
    {
        LOG_E("create User Modbus thread failed !");
        return RT_ERROR;
    }

    return RT_EOK;
}
/*********************************************************************************/

/*********************************************************************************/
/* @brief   从机回调函数
 * @param   ctx modbus 句柄
 * @param   slave_info 从机信息体
 * @return  = 0: 正常;
 *          < 0: 异常
 *             (-AGILE_MODBUS_EXCEPTION_UNKNOW(-255): 未知异常，从机不会打包响应数据)
 *             (其他负数异常码: 从机会打包异常响应数据)
 */
int L_ModbusSlaveCallback(agile_modbus_t *ctx, struct agile_modbus_slave_info *slave_info)
{
    int function = slave_info->sft->function;
    int ret = 0;

    rt_mutex_take(g_Modbus_mux, RT_WAITING_FOREVER);

    switch (function)
    {
        case AGILE_MODBUS_FC_READ_COILS:               //0x01
        case AGILE_MODBUS_FC_READ_DISCRETE_INPUTS:     //0x02
        {
            int address = slave_info->address;
            int nb = slave_info->nb;
            int send_index = slave_info->send_index;
            int is_input = (function == AGILE_MODBUS_FC_READ_DISCRETE_INPUTS);

            for (int now_address = address, i = 0; now_address < address + nb; now_address++, i++)
            {
                if (now_address >= 0 && now_address < MODBUS_S_LEN)
                {
                    int index = now_address - 0;
                    agile_modbus_slave_io_set(ctx->send_buf + send_index, i,
                                              is_input ? g_ModbusSHoldInBitBuf_u8[index] : g_ModbusSHoldBitBuf_u8[index]);
                }
            }
        }
        break;

        case AGILE_MODBUS_FC_READ_HOLDING_REGISTERS:    //0x03
        case AGILE_MODBUS_FC_READ_INPUT_REGISTERS:      //0x04
        {
            int address = slave_info->address;
            int nb = slave_info->nb;
            int send_index = slave_info->send_index;
            int is_input = (function == AGILE_MODBUS_FC_READ_INPUT_REGISTERS);

            for (int now_address = address, i = 0; now_address < address + nb; now_address++, i++)
            {
                if (now_address >= 0 && now_address < MODBUS_S_LEN)
                {
                    int index = now_address - 0;
                    agile_modbus_slave_register_set(ctx->send_buf + send_index, i,
                                                    is_input ? g_ModbusSHoldInBuf_u16[index] : g_ModbusSlaveHoldBuf_u16[index]);
                }
            }
        }
        break;

        case AGILE_MODBUS_FC_WRITE_SINGLE_COIL:         //0x05
        case AGILE_MODBUS_FC_WRITE_MULTIPLE_COILS:      //0x0f
        {
            int address = slave_info->address;

            if (function == AGILE_MODBUS_FC_WRITE_SINGLE_COIL)
            {
                if (address >= 0 && address < MODBUS_S_LEN)
                {
                    int index = address - 0;
                    int data = *((int *)slave_info->buf);
                    g_ModbusSHoldBitBuf_u8[index] = data;
                }
            }
            else
            {
                int nb = slave_info->nb;
                for (int now_address = address, i = 0; now_address < address + nb; now_address++, i++)
                {
                    if (now_address >= 0 && now_address < MODBUS_S_LEN)
                    {
                        int index = now_address - 0;
                        uint8_t status = agile_modbus_slave_io_get(slave_info->buf, i);
                        g_ModbusSHoldBitBuf_u8[index] = status;
                    }
                }
            }
        }
        break;

        case AGILE_MODBUS_FC_WRITE_SINGLE_REGISTER:      //0x06
        case AGILE_MODBUS_FC_WRITE_MULTIPLE_REGISTERS:   //0x10
        {
            int address = slave_info->address;

            if (function == AGILE_MODBUS_FC_WRITE_SINGLE_REGISTER)
            {
                if (address >= 0 && address < MODBUS_S_LEN)
                {
                    int index = address - 0;
                    int data = *((int *)slave_info->buf);
                    g_ModbusSlaveHoldBuf_u16[index] = data;
                }
            }
            else
            {
                int nb = slave_info->nb;
                for (int now_address = address, i = 0; now_address < address + nb; now_address++, i++)
                {
                    if (now_address >= 0 && now_address < MODBUS_S_LEN)
                    {
                        int index = now_address - 0;
                        uint16_t data = agile_modbus_slave_register_get(slave_info->buf, i);
                        g_ModbusSlaveHoldBuf_u16[index] = data;
                    }
                }
            }
        }
        break;

        case AGILE_MODBUS_FC_MASK_WRITE_REGISTER:         //0x16
        {
            int address = slave_info->address;
            if (address >= 0 && address < MODBUS_S_LEN)
            {
                int index = address - 0;
                uint16_t data = g_ModbusSlaveHoldBuf_u16[index];
                uint16_t and = (slave_info->buf[0] << 8) + slave_info->buf[1];
                uint16_t or = (slave_info->buf[2] << 8) + slave_info->buf[3];

                data = (data & and) | (or &(~and));

                g_ModbusSlaveHoldBuf_u16[index] = data;
            }
        }
        break;

        case AGILE_MODBUS_FC_WRITE_AND_READ_REGISTERS:    //0x17
        {
            int address = slave_info->address;
            int nb = (slave_info->buf[0] << 8) + slave_info->buf[1];
            uint16_t address_write = (slave_info->buf[2] << 8) + slave_info->buf[3];
            int nb_write = (slave_info->buf[4] << 8) + slave_info->buf[5];
            int send_index = slave_info->send_index;

            /* Write first. 7 is the offset of the first values to write */
            for (int now_address = address_write, i = 0; now_address < address_write + nb_write; now_address++, i++)
            {
                if (now_address >= 0 && now_address < MODBUS_S_LEN)
                {
                    int index = now_address - 0;
                    uint16_t data = agile_modbus_slave_register_get(slave_info->buf + 7, i);
                    g_ModbusSlaveHoldBuf_u16[index] = data;
                }
            }

            /* and read the data for the response */
            for (int now_address = address, i = 0; now_address < address + nb; now_address++, i++)
            {
                if (now_address >= 0 && now_address < MODBUS_S_LEN)
                {
                    int index = now_address - 0;
                    agile_modbus_slave_register_set(ctx->send_buf + send_index, i, g_ModbusSlaveHoldBuf_u16[index]);
                }
            }
        }
        break;

        default:
            ret = -AGILE_MODBUS_EXCEPTION_ILLEGAL_FUNCTION;
            break;
    }

    rt_mutex_release(g_Modbus_mux);

    return ret;
}
/*********************************************************************************/

/*********************************************************************************/
/* UserModbusSlave线程入口函数
 */
void G_UserModbusSlave_thread_entry(void *parameter)
{
    int l_ReceLen, l_SendLen;

    rt_thread_mdelay(10);

    g_Modbus_mux = rt_mutex_create("modbus_s_mux", RT_IPC_FLAG_FIFO);  //创建数据保护互斥锁

    //agile_modbus
    agile_modbus_rtu_t g_ModbusSlave_s;  //RTU 句柄
    agile_modbus_t *g_pModbusSPar_s = &g_ModbusSlave_s._ctx; //参数

    agile_modbus_rtu_init(&g_ModbusSlave_s, g_ModbusSlaveSendBuf_u8, MODBUS_S_LEN, g_ModbusSlaveReadBuf_u8, MODBUS_S_LEN);
    agile_modbus_set_slave(g_pModbusSPar_s, 1); //从机地址

    //rs485
    rs485_inst_t *l_pRS485S_s = rs485_create("uart4", 9600, 0, MD_S_CTRL_Pin, 1); //串口4，波特率9600，无校验，控制脚，发送模式1（高发低收）
    if (l_pRS485S_s == RT_NULL)
    {
        LOG_E("create rs485S instance fail.");
    }

    rs485_set_recv_tmo(l_pRS485S_s, RT_WAITING_FOREVER); //接收一直等待
    if (rs485_connect(l_pRS485S_s) != RT_EOK)
    {
        rs485_destory(l_pRS485S_s);
        LOG_E("rs485 connect fail.");
    }

    //程序版本
    char l_CommBuf_i8[28] = "20250806.";
    rs485_send(l_pRS485S_s, (void *)l_CommBuf_i8, 25);

    while(1)
    {
        l_ReceLen = rs485_recv(l_pRS485S_s, g_pModbusSPar_s->read_buf, g_pModbusSPar_s->read_bufsz);
        if (l_ReceLen > 6) //串口接收长度
        {
            g_ModbusSlaveHoldBuf_u16[0]++; //测试用

            //生成报文
            l_SendLen = agile_modbus_slave_handle(g_pModbusSPar_s, l_ReceLen, 1, (void *)L_ModbusSlaveCallback, RT_NULL, &l_SendLen);
            if(l_SendLen > 4)
            {
                rs485_send(l_pRS485S_s, g_pModbusSPar_s->send_buf, l_SendLen);
                LOG_D("send len: %d", l_SendLen);

            }
            else
            {
                LOG_W("modbus s tx len: %d", l_SendLen);
            }
        }
        else
        {
            rt_memset(g_pModbusSPar_s->read_buf, 0, abs(l_ReceLen) + 1);
            LOG_W("rec len: %d", l_ReceLen);
        }


        rt_thread_mdelay(20);
    }
}
/*********************************************************************************/

/*********************************************************************************/
/* 初始化UserModbusSlave线程
 */
int G_InitUserModbusSlaveThread_i32()
{
    rt_thread_t thread10 = rt_thread_create("UserModbusSlave_serial", G_UserModbusSlave_thread_entry, RT_NULL, 2048, 23, 50);
    if(thread10 != RT_NULL)
    {
        rt_thread_startup(thread10);
        LOG_I("run User Modbus Slave.");
    }
    else
    {
        LOG_E("create User Modbus Slave thread failed !");
        return RT_ERROR;
    }

    return RT_EOK;
}
/*********************************************************************************/
